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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2008 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS. 026 */ 027package org.opends.server.loggers; 028 029import static org.opends.messages.ConfigMessages.*; 030import static org.opends.server.util.StaticUtils.*; 031 032import java.util.Collection; 033import java.util.List; 034import java.util.concurrent.CopyOnWriteArrayList; 035 036import org.forgerock.i18n.LocalizableMessage; 037import org.forgerock.i18n.LocalizableMessageDescriptor.Arg3; 038import org.forgerock.i18n.slf4j.LocalizedLogger; 039import org.forgerock.opendj.config.server.ConfigChangeResult; 040import org.forgerock.opendj.config.server.ConfigException; 041import org.forgerock.opendj.ldap.ResultCode; 042import org.opends.server.admin.ClassPropertyDefinition; 043import org.opends.server.admin.server.ConfigurationAddListener; 044import org.opends.server.admin.server.ConfigurationChangeListener; 045import org.opends.server.admin.server.ConfigurationDeleteListener; 046import org.opends.server.admin.std.server.LogPublisherCfg; 047import org.opends.server.core.DirectoryServer; 048import org.opends.server.core.ServerContext; 049import org.opends.server.types.DN; 050import org.opends.server.types.InitializationException; 051import org.opends.server.util.StaticUtils; 052 053/** 054 * This class defines the wrapper that will invoke all registered loggers for 055 * each type of request received or response sent. If no log publishers are 056 * registered, messages will be directed to standard out. 057 * 058 * @param <P> 059 * The type of the LogPublisher corresponding to this logger 060 * @param <C> 061 * The type of the LogPublisherCfg corresponding to this logger 062 */ 063public abstract class AbstractLogger 064 <P extends LogPublisher<C>, C extends LogPublisherCfg> 065 implements ConfigurationAddListener<C>, ConfigurationDeleteListener<C>, 066 ConfigurationChangeListener<C> 067{ 068 /** 069 * The storage designed to store log publishers. It is helpful in abstracting 070 * away the methods used to manage the collection. 071 * 072 * @param <P> 073 * The concrete {@link LogPublisher} type 074 * @param <C> 075 * The concrete {@link LogPublisherCfg} type 076 */ 077 protected static class LoggerStorage<P extends LogPublisher<C>, 078 C extends LogPublisherCfg> 079 { 080 /** Defined as public to allow subclasses of {@link AbstractLogger} to instantiate it. */ 081 public LoggerStorage() 082 { 083 super(); 084 } 085 086 /** The set of loggers that have been registered with the server. It will initially be empty. */ 087 private Collection<P> logPublishers = new CopyOnWriteArrayList<>(); 088 089 /** 090 * Add a log publisher to the logger. 091 * 092 * @param publisher 093 * The log publisher to add. 094 */ 095 public synchronized void addLogPublisher(P publisher) 096 { 097 logPublishers.add(publisher); 098 } 099 100 /** 101 * Remove a log publisher from the logger. 102 * 103 * @param publisher 104 * The log publisher to remove. 105 * @return True if the log publisher is removed or false otherwise. 106 */ 107 public synchronized boolean removeLogPublisher(P publisher) 108 { 109 boolean removed = logPublishers.remove(publisher); 110 111 if (removed) 112 { 113 publisher.close(); 114 } 115 116 return removed; 117 } 118 119 /** Removes all existing log publishers from the logger. */ 120 public synchronized void removeAllLogPublishers() 121 { 122 StaticUtils.close(logPublishers); 123 logPublishers.clear(); 124 } 125 126 /** 127 * Returns the logPublishers. 128 * 129 * @return the collection of {@link LogPublisher}s 130 */ 131 public Collection<P> getLogPublishers() 132 { 133 return logPublishers; 134 } 135 } 136 137 /** 138 * Returns the log publishers. 139 * 140 * @return the collection of {@link LogPublisher}s 141 */ 142 protected abstract Collection<P> getLogPublishers(); 143 144 /** 145 * Add a log publisher to the logger. 146 * 147 * @param publisher 148 * The log publisher to add. 149 */ 150 public abstract void addLogPublisher(P publisher); 151 152 /** 153 * Remove a log publisher from the logger. 154 * 155 * @param publisher 156 * The log publisher to remove. 157 * @return True if the log publisher is removed or false otherwise. 158 */ 159 public abstract boolean removeLogPublisher(P publisher); 160 161 /** Removes all existing log publishers from the logger. */ 162 public abstract void removeAllLogPublishers(); 163 164 /** 165 * Returns the java {@link ClassPropertyDefinition} for the current logger. 166 * 167 * @return the java {@link ClassPropertyDefinition} for the current logger. 168 */ 169 protected abstract ClassPropertyDefinition getJavaClassPropertyDefinition(); 170 171 private final Class<P> logPublisherClass; 172 private final Arg3<Object, Object, Object> invalidLoggerClassErrorMessage; 173 private ServerContext serverContext; 174 175 /** 176 * The constructor for this class. 177 * 178 * @param logPublisherClass 179 * the log publisher class 180 * @param invalidLoggerClassErrorMessage 181 * the error message to use if the logger class in invalid 182 */ 183 AbstractLogger( 184 final Class<P> logPublisherClass, 185 final Arg3<Object, Object, Object> 186 invalidLoggerClassErrorMessage) 187 { 188 this.logPublisherClass = logPublisherClass; 189 this.invalidLoggerClassErrorMessage = invalidLoggerClassErrorMessage; 190 } 191 192 /** 193 * Initializes all the log publishers. 194 * 195 * @param configs The log publisher configurations. 196 * @param serverContext 197 * The server context. 198 * @throws ConfigException 199 * If an unrecoverable problem arises in the process of 200 * performing the initialization as a result of the server 201 * configuration. 202 * @throws InitializationException 203 * If a problem occurs during initialization that is not 204 * related to the server configuration. 205 */ 206 public void initializeLogger(List<C> configs, ServerContext serverContext) 207 throws ConfigException, InitializationException 208 { 209 this.serverContext = serverContext; 210 for (C config : configs) 211 { 212 config.addChangeListener((ConfigurationChangeListener) this); 213 214 if (config.isEnabled()) 215 { 216 final P logPublisher = serverContext.getCommonAudit().isCommonAuditConfig(config) ? 217 getLogPublisherForCommonAudit(config) : getLogPublisher(config); 218 addLogPublisher(logPublisher); 219 } 220 } 221 } 222 223 ServerContext getServerContext() 224 { 225 return serverContext; 226 } 227 228 @Override 229 public boolean isConfigurationAddAcceptable(C config, List<LocalizableMessage> unacceptableReasons) 230 { 231 return !config.isEnabled() || isJavaClassAcceptable(config, unacceptableReasons); 232 } 233 234 @Override 235 public boolean isConfigurationChangeAcceptable(C config, List<LocalizableMessage> unacceptableReasons) 236 { 237 return !config.isEnabled() || isJavaClassAcceptable(config, unacceptableReasons); 238 } 239 240 @Override 241 public ConfigChangeResult applyConfigurationAdd(C config) 242 { 243 final ConfigChangeResult ccr = applyConfigurationAdd0(config); 244 if (ccr.getResultCode() == ResultCode.SUCCESS) 245 { 246 config.addChangeListener((ConfigurationChangeListener) this); 247 } 248 return ccr; 249 } 250 251 private ConfigChangeResult applyConfigurationAdd0(C config) 252 { 253 final ConfigChangeResult ccr = new ConfigChangeResult(); 254 255 if (config.isEnabled()) 256 { 257 try 258 { 259 final P logPublisher = serverContext.getCommonAudit().isCommonAuditConfig(config) ? 260 getLogPublisherForCommonAudit(config) : getLogPublisher(config); 261 addLogPublisher(logPublisher); 262 } 263 catch(ConfigException e) 264 { 265 LocalizedLogger.getLoggerForThisClass().traceException(e); 266 ccr.addMessage(e.getMessageObject()); 267 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 268 } 269 catch (Exception e) 270 { 271 LocalizedLogger.getLoggerForThisClass().traceException(e); 272 ccr.addMessage(ERR_CONFIG_LOGGER_CANNOT_CREATE_LOGGER.get(config.dn(), stackTraceToSingleLineString(e))); 273 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 274 } 275 } 276 return ccr; 277 } 278 279 private P findLogPublisher(DN dn) 280 { 281 for (P publisher : getLogPublishers()) 282 { 283 if (publisher.getDN().equals(dn)) 284 { 285 return publisher; 286 } 287 } 288 return null; 289 } 290 291 @Override 292 public ConfigChangeResult applyConfigurationChange(C config) 293 { 294 final ConfigChangeResult ccr = new ConfigChangeResult(); 295 296 P logPublisher = findLogPublisher(config.dn()); 297 if (logPublisher == null) 298 { 299 if (config.isEnabled()) 300 { 301 // Needs to be added and enabled 302 return applyConfigurationAdd0(config); 303 } 304 } 305 else 306 { 307 if (config.isEnabled()) 308 { 309 // Changes to the class name cannot be 310 // applied dynamically, so if the class name did change then 311 // indicate that administrative action is required for that 312 // change to take effect. 313 String className = config.getJavaClass(); 314 if (!className.equals(logPublisher.getClass().getName())) 315 { 316 ccr.setAdminActionRequired(true); 317 } 318 try 319 { 320 CommonAudit commonAudit = serverContext.getCommonAudit(); 321 if (commonAudit.isCommonAuditConfig(config)) 322 { 323 commonAudit.addOrUpdatePublisher(config); 324 } // else the publisher is currently active, so we don't need to do anything. 325 } 326 catch (Exception e) 327 { 328 LocalizedLogger.getLoggerForThisClass().traceException(e); 329 ccr.addMessage(ERR_CONFIG_LOGGER_CANNOT_UPDATE_LOGGER.get(config.dn(), stackTraceToSingleLineString(e))); 330 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 331 } 332 } 333 else 334 { 335 // The publisher is being disabled so shut down and remove. 336 return applyConfigurationDelete(config); 337 } 338 } 339 return ccr; 340 } 341 342 @Override 343 public boolean isConfigurationDeleteAcceptable(C config, List<LocalizableMessage> unacceptableReasons) 344 { 345 return findLogPublisher(config.dn()) != null; 346 } 347 348 @Override 349 public ConfigChangeResult applyConfigurationDelete(C config) 350 { 351 final ConfigChangeResult ccr = new ConfigChangeResult(); 352 353 P logPublisher = findLogPublisher(config.dn()); 354 if(logPublisher != null) 355 { 356 removeLogPublisher(logPublisher); 357 try 358 { 359 CommonAudit commonAudit = serverContext.getCommonAudit(); 360 if (commonAudit.isExistingCommonAuditConfig(config)) 361 { 362 commonAudit.removePublisher(config); 363 } 364 } 365 catch (ConfigException e) 366 { 367 LocalizedLogger.getLoggerForThisClass().traceException(e); 368 ccr.addMessage(ERR_CONFIG_LOGGER_CANNOT_DELETE_LOGGER.get(config.dn(), stackTraceToSingleLineString(e))); 369 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 370 } 371 } 372 else 373 { 374 ccr.setResultCode(ResultCode.NO_SUCH_OBJECT); 375 } 376 377 return ccr; 378 } 379 380 private boolean isJavaClassAcceptable(C config, 381 List<LocalizableMessage> unacceptableReasons) 382 { 383 String className = config.getJavaClass(); 384 ClassPropertyDefinition pd = getJavaClassPropertyDefinition(); 385 try { 386 P publisher = pd.loadClass(className, logPublisherClass).newInstance(); 387 return publisher.isConfigurationAcceptable(config, unacceptableReasons); 388 } catch (Exception e) { 389 unacceptableReasons.add(invalidLoggerClassErrorMessage.get(className, config.dn(), e)); 390 return false; 391 } 392 } 393 394 private P getLogPublisher(C config) throws ConfigException 395 { 396 String className = config.getJavaClass(); 397 ClassPropertyDefinition pd = getJavaClassPropertyDefinition(); 398 try { 399 P logPublisher = pd.loadClass(className, logPublisherClass).newInstance(); 400 logPublisher.initializeLogPublisher(config, serverContext); 401 return logPublisher; 402 } 403 catch (Exception e) 404 { 405 throw new ConfigException( 406 invalidLoggerClassErrorMessage.get(className, config.dn(), e), e); 407 } 408 } 409 410 private P getLogPublisherForCommonAudit(C config) throws InitializationException, ConfigException 411 { 412 CommonAudit commonAudit = serverContext.getCommonAudit(); 413 commonAudit.addOrUpdatePublisher(config); 414 P logPublisher = getLogPublisher(config); 415 CommonAuditLogPublisher publisher = (CommonAuditLogPublisher) logPublisher; 416 publisher.setRequestHandler(commonAudit.getRequestHandler(config)); 417 return logPublisher; 418 } 419}