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 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.loggers; 028 import org.opends.messages.Message; 029 030 import java.util.concurrent.CopyOnWriteArrayList; 031 import java.util.List; 032 import java.util.ArrayList; 033 import java.lang.reflect.Method; 034 import java.lang.reflect.InvocationTargetException; 035 036 import org.opends.server.api.DirectoryThread; 037 import org.opends.server.api.ErrorLogPublisher; 038 import org.opends.server.backends.task.Task; 039 import org.opends.server.loggers.debug.DebugTracer; 040 041 import org.opends.server.types.*; 042 import org.opends.server.admin.std.server.ErrorLogPublisherCfg; 043 import org.opends.server.admin.std.meta.ErrorLogPublisherCfgDefn; 044 import org.opends.server.admin.server.ConfigurationAddListener; 045 import org.opends.server.admin.server.ConfigurationDeleteListener; 046 import org.opends.server.admin.server.ConfigurationChangeListener; 047 import org.opends.server.admin.ClassPropertyDefinition; 048 import org.opends.server.config.ConfigException; 049 import org.opends.server.core.DirectoryServer; 050 051 import static org.opends.server.loggers.debug.DebugLogger.*; 052 import static org.opends.messages.ConfigMessages.*; 053 import static org.opends.server.util.StaticUtils.*; 054 /** 055 * This class defines the wrapper that will invoke all registered error loggers 056 * for each type of request received or response sent. If no error log 057 * publishers are registered, messages will be directed to standard out. 058 */ 059 public class ErrorLogger implements 060 ConfigurationAddListener<ErrorLogPublisherCfg>, 061 ConfigurationDeleteListener<ErrorLogPublisherCfg>, 062 ConfigurationChangeListener<ErrorLogPublisherCfg> 063 { 064 /** 065 * The tracer object for the debug logger. 066 */ 067 private static final DebugTracer TRACER = getTracer(); 068 069 // The set of error loggers that have been registered with the server. It 070 // will initially be empty. 071 private static CopyOnWriteArrayList<ErrorLogPublisher> errorPublishers = 072 new CopyOnWriteArrayList<ErrorLogPublisher>(); 073 074 // The singleton instance of this class for configuration purposes. 075 private static final ErrorLogger instance = new ErrorLogger(); 076 077 /** 078 * Retrieve the singleton instance of this class. 079 * 080 * @return The singleton instance of this logger. 081 */ 082 public static ErrorLogger getInstance() 083 { 084 return instance; 085 } 086 087 /** 088 * Add an error log publisher to the error logger. 089 * 090 * @param publisher The error log publisher to add. 091 */ 092 public synchronized static void addErrorLogPublisher( 093 ErrorLogPublisher publisher) 094 { 095 errorPublishers.add(publisher); 096 } 097 098 /** 099 * Remove an error log publisher from the error logger. 100 * 101 * @param publisher The error log publisher to remove. 102 * @return True if the error log publisher is removed or false otherwise. 103 */ 104 public synchronized static boolean removeErrorLogPublisher( 105 ErrorLogPublisher publisher) 106 { 107 boolean removed = errorPublishers.remove(publisher); 108 109 if(removed) 110 { 111 publisher.close(); 112 } 113 114 return removed; 115 } 116 117 /** 118 * Removes all existing error log publishers from the logger. 119 */ 120 public synchronized static void removeAllErrorLogPublishers() 121 { 122 for(ErrorLogPublisher publisher : errorPublishers) 123 { 124 publisher.close(); 125 } 126 127 errorPublishers.clear(); 128 } 129 130 /** 131 * Initializes all the error log publishers. 132 * 133 * @param configs The error log publisher configurations. 134 * @throws ConfigException 135 * If an unrecoverable problem arises in the process of 136 * performing the initialization as a result of the server 137 * configuration. 138 * @throws InitializationException 139 * If a problem occurs during initialization that is not 140 * related to the server configuration. 141 */ 142 public void initializeErrorLogger(List<ErrorLogPublisherCfg> configs) 143 throws ConfigException, InitializationException 144 { 145 for(ErrorLogPublisherCfg config : configs) 146 { 147 config.addErrorChangeListener(this); 148 149 if(config.isEnabled()) 150 { 151 ErrorLogPublisher errorLogPublisher = getErrorPublisher(config); 152 153 addErrorLogPublisher(errorLogPublisher); 154 } 155 } 156 } 157 158 /** 159 * {@inheritDoc} 160 */ 161 public boolean isConfigurationAddAcceptable(ErrorLogPublisherCfg config, 162 List<Message> unacceptableReasons) 163 { 164 return !config.isEnabled() || 165 isJavaClassAcceptable(config, unacceptableReasons); 166 } 167 168 /** 169 * {@inheritDoc} 170 */ 171 public boolean isConfigurationChangeAcceptable( 172 ErrorLogPublisherCfg config, 173 List<Message> unacceptableReasons) 174 { 175 return !config.isEnabled() || 176 isJavaClassAcceptable(config, unacceptableReasons); 177 } 178 179 /** 180 * {@inheritDoc} 181 */ 182 public ConfigChangeResult applyConfigurationAdd(ErrorLogPublisherCfg config) 183 { 184 // Default result code. 185 ResultCode resultCode = ResultCode.SUCCESS; 186 boolean adminActionRequired = false; 187 ArrayList<Message> messages = new ArrayList<Message>(); 188 189 config.addErrorChangeListener(this); 190 191 if(config.isEnabled()) 192 { 193 try 194 { 195 ErrorLogPublisher errorLogPublisher = getErrorPublisher(config); 196 197 addErrorLogPublisher(errorLogPublisher); 198 } 199 catch(ConfigException e) 200 { 201 if (debugEnabled()) 202 { 203 TRACER.debugCaught(DebugLogLevel.ERROR, e); 204 } 205 messages.add(e.getMessageObject()); 206 resultCode = DirectoryServer.getServerErrorResultCode(); 207 } 208 catch (Exception e) 209 { 210 if (debugEnabled()) 211 { 212 TRACER.debugCaught(DebugLogLevel.ERROR, e); 213 } 214 messages.add(ERR_CONFIG_LOGGER_CANNOT_CREATE_LOGGER.get( 215 String.valueOf(config.dn().toString()), 216 stackTraceToSingleLineString(e))); 217 resultCode = DirectoryServer.getServerErrorResultCode(); 218 } 219 } 220 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 221 } 222 223 /** 224 * {@inheritDoc} 225 */ 226 public ConfigChangeResult applyConfigurationChange( 227 ErrorLogPublisherCfg config) 228 { 229 // Default result code. 230 ResultCode resultCode = ResultCode.SUCCESS; 231 boolean adminActionRequired = false; 232 ArrayList<Message> messages = new ArrayList<Message>(); 233 234 DN dn = config.dn(); 235 236 ErrorLogPublisher errorLogPublisher = null; 237 for(ErrorLogPublisher publisher : errorPublishers) 238 { 239 if(publisher.getDN().equals(dn)) 240 { 241 errorLogPublisher = publisher; 242 break; 243 } 244 } 245 246 if(errorLogPublisher == null) 247 { 248 if(config.isEnabled()) 249 { 250 // Needs to be added and enabled. 251 return applyConfigurationAdd(config); 252 } 253 } 254 else 255 { 256 if(config.isEnabled()) 257 { 258 // The publisher is currently active, so we don't need to do anything. 259 // Changes to the class name cannot be 260 // applied dynamically, so if the class name did change then 261 // indicate that administrative action is required for that 262 // change to take effect. 263 String className = config.getJavaClass(); 264 if(!className.equals(errorLogPublisher.getClass().getName())) 265 { 266 adminActionRequired = true; 267 } 268 } 269 else 270 { 271 // The publisher is being disabled so shut down and remove. 272 removeErrorLogPublisher(errorLogPublisher); 273 } 274 } 275 276 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 277 } 278 279 /** 280 * {@inheritDoc} 281 */ 282 public boolean isConfigurationDeleteAcceptable( 283 ErrorLogPublisherCfg config, 284 List<Message> unacceptableReasons) 285 { 286 DN dn = config.dn(); 287 288 ErrorLogPublisher errorLogPublisher = null; 289 for(ErrorLogPublisher publisher : errorPublishers) 290 { 291 if(publisher.getDN().equals(dn)) 292 { 293 errorLogPublisher = publisher; 294 break; 295 } 296 } 297 298 return errorLogPublisher != null; 299 } 300 301 /** 302 * {@inheritDoc} 303 */ 304 public ConfigChangeResult applyConfigurationDelete( 305 ErrorLogPublisherCfg config) 306 { 307 // Default result code. 308 ResultCode resultCode = ResultCode.SUCCESS; 309 boolean adminActionRequired = false; 310 311 ErrorLogPublisher errorLogPublisher = null; 312 for(ErrorLogPublisher publisher : errorPublishers) 313 { 314 if(publisher.getDN().equals(config.dn())) 315 { 316 errorLogPublisher = publisher; 317 break; 318 } 319 } 320 321 if(errorLogPublisher != null) 322 { 323 removeErrorLogPublisher(errorLogPublisher); 324 } 325 else 326 { 327 resultCode = ResultCode.NO_SUCH_OBJECT; 328 } 329 330 return new ConfigChangeResult(resultCode, adminActionRequired); 331 } 332 333 private boolean isJavaClassAcceptable(ErrorLogPublisherCfg config, 334 List<Message> unacceptableReasons) 335 { 336 String className = config.getJavaClass(); 337 ErrorLogPublisherCfgDefn d = ErrorLogPublisherCfgDefn.getInstance(); 338 ClassPropertyDefinition pd = 339 d.getJavaClassPropertyDefinition(); 340 // Load the class and cast it to a DebugLogPublisher. 341 ErrorLogPublisher publisher = null; 342 Class<? extends ErrorLogPublisher> theClass; 343 try { 344 theClass = pd.loadClass(className, ErrorLogPublisher.class); 345 publisher = theClass.newInstance(); 346 } catch (Exception e) { 347 Message message = ERR_CONFIG_LOGGER_INVALID_ERROR_LOGGER_CLASS.get( 348 className, 349 config.dn().toString(), 350 String.valueOf(e)); 351 unacceptableReasons.add(message); 352 return false; 353 } 354 // Check that the implementation class implements the correct interface. 355 try { 356 // Determine the initialization method to use: it must take a 357 // single parameter which is the exact type of the configuration 358 // object. 359 Method method = theClass.getMethod("isConfigurationAcceptable", 360 ErrorLogPublisherCfg.class, 361 List.class); 362 Boolean acceptable = (Boolean) method.invoke(publisher, config, 363 unacceptableReasons); 364 365 if (! acceptable) 366 { 367 return false; 368 } 369 } catch (Exception e) { 370 Message message = ERR_CONFIG_LOGGER_INVALID_ERROR_LOGGER_CLASS.get( 371 className, 372 config.dn().toString(), 373 String.valueOf(e)); 374 unacceptableReasons.add(message); 375 return false; 376 } 377 // The class is valid as far as we can tell. 378 return true; 379 } 380 381 private ErrorLogPublisher getErrorPublisher(ErrorLogPublisherCfg config) 382 throws ConfigException { 383 String className = config.getJavaClass(); 384 ErrorLogPublisherCfgDefn d = ErrorLogPublisherCfgDefn.getInstance(); 385 ClassPropertyDefinition pd = 386 d.getJavaClassPropertyDefinition(); 387 // Load the class and cast it to a ErrorLogPublisher. 388 Class<? extends ErrorLogPublisher> theClass; 389 ErrorLogPublisher errorLogPublisher; 390 try { 391 theClass = pd.loadClass(className, ErrorLogPublisher.class); 392 errorLogPublisher = theClass.newInstance(); 393 394 // Determine the initialization method to use: it must take a 395 // single parameter which is the exact type of the configuration 396 // object. 397 Method method = theClass.getMethod("initializeErrorLogPublisher", config 398 .configurationClass()); 399 method.invoke(errorLogPublisher, config); 400 } 401 catch (InvocationTargetException ite) 402 { 403 // Rethrow the exceptions thrown be the invoked method. 404 Throwable e = ite.getTargetException(); 405 Message message = ERR_CONFIG_LOGGER_INVALID_ERROR_LOGGER_CLASS.get( 406 className, config.dn().toString(), stackTraceToSingleLineString(e)); 407 throw new ConfigException(message, e); 408 } 409 catch (Exception e) 410 { 411 Message message = ERR_CONFIG_LOGGER_INVALID_ERROR_LOGGER_CLASS.get( 412 className, config.dn().toString(), String.valueOf(e)); 413 throw new ConfigException(message, e); 414 } 415 416 // The error publisher has been successfully initialized. 417 return errorLogPublisher; 418 } 419 420 421 422 /** 423 * Writes a message to the error log using the provided information. 424 * 425 * @param message The message to be logged. 426 */ 427 public static void logError(Message message) 428 { 429 for (ErrorLogPublisher publisher : errorPublishers) 430 { 431 publisher.logError(message); 432 } 433 434 if (Thread.currentThread() instanceof DirectoryThread) 435 { 436 DirectoryThread thread = (DirectoryThread) Thread.currentThread(); 437 Task task = thread.getAssociatedTask(); 438 if (task != null) 439 { 440 task.addLogMessage(message); 441 } 442 } 443 } 444 } 445