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